package com.arthas.arthasforsba.utils;


import com.arthas.arthasforsba.exception.BaseException;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.crypto.digests.SM3Digest;

import java.util.Arrays;

/**
 * @author dingling
 * @desc sm3 用于密码加密，与md5国际加密算法相似，均为不可逆
 * @date 2023-07-13 10:40
 **/

public class Sm3Util {

    private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    private static final String ENCODING = "UTF-8";

    public static String sm3Encode(String str) {
        byte[] md = new byte[32];
        byte[] msg1 = str.getBytes();
        SM3Digest sm3 = new SM3Digest();
        sm3.update(msg1, 0, msg1.length);
        sm3.doFinal(md, 0);
        Hex hex = new Hex();
        return new String(hex.encode(md));
    }


    /**
     * SM3加密
     *
     * @param src 明文
     * @return 密文
     */
    public static String encrypt(String src) {
        try {
            return bytesToHex(getEncryptBySrcByte(src.getBytes()));
        } catch (Exception e) {
            throw new BaseException("未知异常，数据加密失败！" + e.getMessage());
        }
    }


    public static String encrypt(CharSequence src) {
        if (src instanceof String) {
            return encrypt((String) src);
        }
        throw new BaseException("暂不支持该类型数据！");
    }


    /**
     * SM3数据校验
     *
     * @param src       源数据
     * @param sm3HexStr 16进制的加密数据
     * @return 是否校验成功
     */
    public static boolean verify(String src, String sm3HexStr) {
        try {
            byte[] sm3HashCode = fromHexString(sm3HexStr);
            byte[] newHashCode = getEncryptBySrcByte(src.getBytes(ENCODING));
            return Arrays.equals(newHashCode, sm3HashCode);
        } catch (Exception e) {
            System.out.println("SM3校验失败，数据被篡改！");
        }
        return false;
    }

    public static boolean verify(CharSequence src, String sm3HexStr) {
        if (src instanceof String) {
            return verify((String) src, sm3HexStr);
        }
        throw new BaseException("暂不支持该类型数据！");
    }


    /**
     * 返回长度为32位的加密后的byte数组
     *
     * @param srcByte 原始 byte[]
     * @return 加密byte[]
     */
    public static byte[] getEncryptBySrcByte(byte[] srcByte) {
        SM3Digest sm3 = new SM3Digest();
        sm3.update(srcByte, 0, srcByte.length);
        byte[] encryptByte = new byte[sm3.getDigestSize()];
        sm3.doFinal(encryptByte, 0);
        return encryptByte;
    }

    public static String bytesToHex(byte[] bytes) {
        char[] buf = new char[bytes.length * 2];
        int index = 0;
        for (byte b : bytes) {
            buf[index++] = HEX_CHAR[b >>> 4 & 0xf];
            buf[index++] = HEX_CHAR[b & 0xf];
        }

        return new String(buf);
    }

    public static byte[] hexStringToBytes(String str) {
        if (str == null || "".equals(str.trim())) {
            return new byte[0];
        }

        byte[] bytes = new byte[str.length() / 2];
        for (int i = 0; i < str.length() / 2; i++) {
            String subStr = str.substring(i * 2, i * 2 + 2);
            bytes[i] = (byte) Integer.parseInt(subStr, 16);
        }

        return bytes;
    }

    public static byte[] fromHexString(String var0) {
        char[] var1 = var0.toUpperCase().toCharArray();
        int var2 = 0;

        for (char value : var1) {
            if (value >= '0' && value <= '9' || value >= 'A' && value <= 'F') {
                ++var2;
            }
        }

        byte[] var6 = new byte[var2 + 1 >> 1];
        int var4 = var2 & 1;

        for (char c : var1) {
            if (c >= '0' && c <= '9') {
                var6[var4 >> 1] = (byte) (var6[var4 >> 1] << 4);
                var6[var4 >> 1] = (byte) (var6[var4 >> 1] | c - 48);
            } else {
                if (c < 'A' || c > 'F') {
                    continue;
                }

                var6[var4 >> 1] = (byte) (var6[var4 >> 1] << 4);
                var6[var4 >> 1] = (byte) (var6[var4 >> 1] | c - 65 + 10);
            }

            ++var4;
        }

        return var6;
    }

    public static void main(String[] args) {
        try {
            boolean res = Sm3Util.verify("Admin@123456", "8e60ce8e112c7854cdbef65b951606d51852d5aa5df3a64250ba6faa3c75be2d");
            String res2 = Sm3Util.encrypt("Admin@123456");
            System.out.println(res);
            System.out.println(res2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}